package com.mobimagic.lockscreen.view;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;

import com.mobimagic.lockscreen.AppEnv;
import com.mobimagic.lockscreen.Charge;
import com.mobimagic.lockscreen.util.ScreenUtil;
import com.nineoldandroids.animation.ValueAnimator;
import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener;

public class Bubbles implements AnimatorUpdateListener {

    private static final String TAG = "Bubbles";
    private static final boolean DEBUG = AppEnv.bAppdebug;
    private static final int DEFAULT_BUBBLES_COUNT = 15;
    private static final int MAX_SIZE = 45;
    private List<Bubble> bubbles = new LinkedList<Bubble>();
    private Random random = new Random();
    private int width, height;
    private Paint paint = new Paint();
    private int disableHeigth;
    private int addHeight;
    private Charge charge;
    private int topGap;
    private int leftLimit, rightLimit;
    private Rect dirty = new Rect();
    private int bottomGap;
    private boolean drawed = false;
    private ValueAnimator valueAnimator;
    private float radiusMax;
    private float speedYMax;
    private int parentWidth;
    private View view;
    private List<Bubble> prepareBubbles = new ArrayList<Bubble>();

    public Bubbles(View view) {
        this.view = view;
        radiusMax = ScreenUtil.dpToPx(view.getContext(), 5f);
        speedYMax = ScreenUtil.dpToPx(view.getContext(), 2f);
        paint.setAntiAlias(true);
    }

    public void setTopGap(int topGap) {
        this.topGap = topGap;
    }

    public void setBounds(int parentWidth, int parentHeihgt) {
        this.parentWidth = parentWidth;

        width = (int) (radiusMax * 16f);
        height = parentHeihgt;
        leftLimit = (parentWidth - width) / 2;
        rightLimit = leftLimit + width;
        dirty.left = leftLimit;
        dirty.right = rightLimit;
        dirty.bottom = height;
        measureDisbaleHeight();
        drawed = false;

    }

    private void addBubbles() {
        List<Bubble> theBubbles;
        if (height <= 0) {

            theBubbles = prepareBubbles;
        } else {
            theBubbles = bubbles;
        }
        boolean isEmpty = theBubbles.isEmpty();
        for (int i = 0; i < DEFAULT_BUBBLES_COUNT; i++) {
            Bubble bubble = new Bubble();
            initBubble(bubble);
            bubble.setVisible(isEmpty && i == 0);
            theBubbles.add(bubble);
            if (theBubbles.size() >= MAX_SIZE) {
                break;
            }
        }
    }

    private boolean knockTop(Bubble bubbleItem) {
        return bubbleItem.getY() - bubbleItem.getRadius() - disableHeigth <= 0;
    }

    private boolean knockLeft(Bubble bubbleItem) {
        return bubbleItem.getX() + bubbleItem.getSpeedX() <= leftLimit + bubbleItem.getRadius();
    }

    private boolean knockRight(Bubble bubbleItem) {

        return bubbleItem.getX() + bubbleItem.getSpeedX() >= rightLimit - bubbleItem.getRadius();
    }

    private boolean needShowOneBubble(Bubble lastVisibleBubble) {
        return lastVisibleBubble.getY() < addHeight;
    }

    private void updateValue() {

        int visibleCount = 0;
        Bubble lastVisibleBubble = null;

        Iterator<Bubble> iterator = bubbles.iterator();

        Bubble firstBubbleItem = null;
        while (iterator.hasNext()) {
            Bubble bubbleItem = iterator.next();
            if (firstBubbleItem == null) {
                firstBubbleItem = bubbleItem;
            }
            if (bubbleItem.isVisible()) {
                boolean suc = updateValues(iterator, bubbleItem);

                if (suc) {
                    visibleCount++;
                    lastVisibleBubble = bubbleItem;

                }
            } else {
                if (lastVisibleBubble != null) {
                    if (needShowOneBubble(lastVisibleBubble)) {
                        bubbleItem.setVisible(true);
                        visibleCount++;
                    }
                    lastVisibleBubble = null;
                }

            }

        }
        if (visibleCount == 0 && firstBubbleItem != null) {
            firstBubbleItem.setVisible(true);
            visibleCount++;
        }

        if (visibleCount > 0) {
            view.invalidate(dirty);
        } else {
            bubbles.clear();
            stop();
        }

    }

    /**
     * @param iterator
     * @param bubbleItem
     * @return 是否成功
     */
    private boolean updateValues(Iterator<Bubble> iterator, Bubble bubbleItem) {
        if (knockTop(bubbleItem)) {//碰到上边界从数组中移除
            iterator.remove();
            bubbleItem.setVisible(false);
            return false;

        } else {
            boolean suc = updateY(bubbleItem);
            if (!suc) {
                iterator.remove();
                bubbleItem.setVisible(false);
                return false;
            }
            updateX(bubbleItem);
            float speedX = bubbleItem.getSpeedX() + (random.nextFloat() - 0.5f) * 0.1f;
            bubbleItem.setSpeedX(speedX);
        }
        return true;
    }

    private boolean updateY(Bubble bubbleItem) {
        bubbleItem.setY(bubbleItem.getY() - bubbleItem.getSpeedY());
        float radius = bubbleItem.getRadius();
        if (knockTop(bubbleItem)) {
            return false;

        } else {
            if (radius <= radiusMax) {
                float newRadius = radius + 0.01f * radiusMax * random.nextFloat();
                if (newRadius > radiusMax) {
                    newRadius = radiusMax;
                }
                bubbleItem.setRadius(newRadius);
            }
        }
        return true;
    }

    private void updateX(Bubble bubbleItem) {
        if (knockLeft(bubbleItem)) {//最左侧
            bubbleItem.setX(bubbleItem.getRadius() + leftLimit);
            bubbleItem.setSpeedX(-bubbleItem.getSpeedX());
        } else if (knockRight(bubbleItem)) {//最右侧
            bubbleItem.setX(rightLimit - bubbleItem.getRadius());
            bubbleItem.setSpeedX(-bubbleItem.getSpeedX());
        } else {
            bubbleItem.setX(bubbleItem.getX() + bubbleItem.getSpeedX());
        }
    }

    private void stop() {
        if (MobileChargingView.DEBUG) {
            Log.d(MobileChargingView.TAG, "bubbles stop!");
        }
        if (valueAnimator != null) {
            valueAnimator.cancel();
            valueAnimator = null;
        }
    }

    private void start() {
        if (MobileChargingView.DEBUG) {
            Log.d(MobileChargingView.TAG, "bubbles start!");
        }

        if (valueAnimator != null) {
            return;
        }
        valueAnimator = ValueAnimator.ofFloat(0f, 1f);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
        valueAnimator.setRepeatCount(-1);
        valueAnimator.addUpdateListener(this);
        valueAnimator.start();
    }

    private Bubble initBubble(Bubble bubble) {
        int alpha = random.nextInt(90) + 60;
        bubble.setColor(Color.argb(alpha, 255, 255, 255));
        float radius = radiusMax / 3;
        float speedY = random.nextFloat() * speedYMax + speedYMax;
        bubble.setRadius(radius);
        bubble.setSpeedY(speedY);
        bubble.setX((leftLimit + rightLimit) / 2);
        bubble.setY(height);
        bubble.setVisible(true);
        return bubble;
    }

    public void setCharge(Charge charge) {
        this.charge = charge;
        measureDisbaleHeight();
    }

    private void measureDisbaleHeight() {
        float value = charge.getChargingValue();
        disableHeigth = topGap + (height - bottomGap - topGap) - (int) ((height - bottomGap - topGap) * value);
        addHeight = height - bottomGap - ScreenUtil.dpToPxInt(view.getContext(), 30);
        dirty.top = disableHeigth;
    }

    private void preDraw() {
        if (!prepareBubbles.isEmpty()) {
            for (Bubble bubble : prepareBubbles) {
                bubble.setY(height);
                bubble.setX((leftLimit + rightLimit) / 2);
                bubbles.add(bubble);
            }
            prepareBubbles.clear();
            start();
        }

    }

    public void draw(Canvas canvas) {
        if (!drawed) {
            drawed = true;
            preDraw();
        }
        canvas.save();
        canvas.clipRect(dirty);
        for (Bubble bubble : bubbles) {
            bubble.draw(canvas, paint);
        }
        canvas.restore();

    }

    public void setBottomGap(int bottomGap) {
        this.bottomGap = bottomGap;
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        updateValue();
    }

    public void onStop() {
        if (MobileChargingBGView.SAVE_MODE) {
            stop();
        }
    }

    public void onStart() {
        if (MobileChargingBGView.SAVE_MODE) {
            addBubbles();
            start();
        }
    }

    public void onAttachedToWindow() {
        if (!MobileChargingBGView.SAVE_MODE) {
            addBubbles();
            start();
        }

    }

    public void onDetachedFromWindow() {
        if (!MobileChargingBGView.SAVE_MODE) {
            stop();
        }
    }

}
